package main

import (
	"bytes"
	"errors"
	"fmt"
	//	"io"
	"os"
	"os/exec"
	"os/signal"
	"runtime/debug"
	"strconv"
	"sync"
	"syscall"
	"time"
)

func main() {
	go func() {
		time.Sleep(5 * time.Second)
		sigSendingDemo()
	}()
	sigHandleDemo()
}

func sigHandleDemo() {
	sigRecv1 := make(chan os.Signal, 1)
	sig1 := []os.Signal{syscall.SIGINT, syscall.SIGQUIT}
	fmt.Printf("Set notification for %s ...[sigRecv1]\n", sig1)
	signal.Notify(sigRecv1, sig1...)

	sigRecv2 := make(chan os.Signal, 1)
	sig2 := []os.Signal{syscall.SIGQUIT}
	fmt.Printf("Set notification for %s ...[sigRecv2]\n", sig2)
	signal.Notify(sigRecv2, sig2...)

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		for sig := range sigRecv1 {
			fmt.Printf("Received a signal from sigRecv1 :%s\n", sig)
		}
		fmt.Printf("End .[sigRecv1]\n")
		wg.Done()
	}()
	go func() {
		for sig := range sigRecv2 {
			fmt.Printf("Received a signal from sigRecv2 :%s\n", sig)
		}
		fmt.Printf("End .[sigRecv2]\n")
		wg.Done()
	}()

	fmt.Printf(" Wait for two second ...\n")
	time.Sleep(2 * time.Second)
	fmt.Printf("Stop notification ...\n")
	signal.Stop(sigRecv1)
	close(sigRecv1)
	fmt.Printf("done [sigRecv1]\n")

	wg.Wait()
}

func sigSendingDemo() {

	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("Fatal Error :%s\n", err)
			debug.PrintStack()
		}
	}()
	cmds := []*exec.Cmd{
		exec.Command("ps", "aux"),
		exec.Command("grep", "mysignal"),
		exec.Command("grep", "-v", "grep"),
		exec.Command("grep", "-v", "go run"),
		exec.Command("awk", "{print $2}"),
	}
	output, err := runCmds(cmds)
	if err != nil {
		fmt.Printf("Command Execution Error :%s\n", err)
		return
	}

	pid, err := strconv.Atoi(output)

	if err != nil {
		fmt.Printf("Pid Parsing Error :%s\n", err)
		return
	}

	fmt.Printf("Target PID(s) :\n%v\n", pid)
	proc, err := os.FindProcess(pid)
	if err != nil {
		fmt.Printf("Process finding Error :%s\n", err)
		return
	}
	sig := syscall.SIGQUIT
	fmt.Printf("Send signal %s to the process (PID = %d)...\n", sig, pid)
	err = proc.Signal(sig)
	if err != nil {
		fmt.Printf("Signal Sending Error :%s\n", err)
		return
	}

}

func runCmds(cmds []*exec.Cmd) (string, error) {
	if cmds == nil || len(cmds) == 0 {
		return "", errors.New("The command slice is invalid!")
	}
	cmdLen := len(cmds)

	var output []byte
	first := true

	for i := 0; i < cmdLen; i++ {
		fmt.Printf("Run command :%v...\n", getCmdText(cmds[i]))
		if !first {
			var stdinBuf bytes.Buffer
			stdinBuf.Write(output)
			cmds[i].Stdin = &stdinBuf
		}
		var stdoutBuf bytes.Buffer
		cmds[i].Stdout = &stdoutBuf
		if err := cmds[i].Start(); err != nil {
			fmt.Printf("err in start\n")
			return "", err
		}
		if err := cmds[i].Wait(); err != nil {
			fmt.Printf("err in wait\n")
			return "", err
		}
		output = stdoutBuf.Bytes()

		if first {
			first = false
		}
	}

	var outputBuf bytes.Buffer
	outputBuf.Write(output)

	fmt.Printf("outputBuf :%s\n", outputBuf.String())
	ret := outputBuf.String()

	ret = ret[:len(ret)-1]
	return string(ret), nil
}

func getCmdText(cmd *exec.Cmd) string {
	var buf bytes.Buffer
	buf.WriteString(cmd.Path)

	for _, arg := range cmd.Args[1:] {
		buf.WriteRune(' ')
		buf.WriteString(arg)
	}
	return buf.String()
}
